package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.list.dao.GoodsDao;
import com.atguigu.gmall.list.service.ListService;
import com.atguigu.gmall.model.list.*;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuAttrValue;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class ListServiceImpl implements ListService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;//高级别客户端   最近
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;//高级别客户端   最近
    @Autowired
    private GoodsDao goodsDao;
    @Autowired
    private ProductFeignClient productFeignClient;


    //上架
    @Override
    public void onSale(Long skuId) {
        Goods goods = new Goods();
        //1.查询SkuInfo
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
        goods.setId(skuInfo.getId());
        goods.setDefaultImg(skuInfo.getSkuDefaultImg());
        goods.setTitle(skuInfo.getSkuName());
        goods.setPrice(skuInfo.getPrice().doubleValue());
        //2.时间:添加索引的时间
        Calendar calendar = Calendar.getInstance();
        goods.setCreateTime(calendar.getTime());
        //3.品牌
        Long tmId = skuInfo.getTmId();
        BaseTrademark baseTrademark = productFeignClient.getBaseTrademark(tmId);
        goods.setTmId(baseTrademark.getId());
        goods.setTmName(baseTrademark.getTmName());
        goods.setTmLogoUrl(baseTrademark.getLogoUrl());
        //4.一二三级分类
        BaseCategoryView baseCategoryView = productFeignClient.getBaseCategoryView(skuId);
        goods.setCategory1Id(baseCategoryView.getCategory1Id());
        goods.setCategory1Name(baseCategoryView.getCategory1Name());
        goods.setCategory2Id(baseCategoryView.getCategory2Id());
        goods.setCategory2Name(baseCategoryView.getCategory2Name());
        goods.setCategory3Id(baseCategoryView.getCategory3Id());
        goods.setCategory3Name(baseCategoryView.getCategory3Name());
        //5.平台属性集合
        List<SkuAttrValue> list = productFeignClient.getSkuAttrValueAndAttrAndAttrValue(skuId);
        List<SearchAttr> attrs = list.stream().map((skuAttrValue) -> {
            SearchAttr searchAttr = new SearchAttr();
            searchAttr.setAttrId(skuAttrValue.getBaseAttrInfo().getId());
            searchAttr.setAttrName(skuAttrValue.getBaseAttrInfo().getAttrName());
            searchAttr.setAttrValue(skuAttrValue.getBaseAttrValue().getValueName());
            return searchAttr;
        }).collect(Collectors.toList());
        goods.setAttrs(attrs);
        //保存一个
        goodsDao.save(goods);
    }

    //下架
    @Override
    public void cancelSale(Long skuId) {
        goodsDao.deleteById(skuId);
    }

    //商品热度评分 (加分接口)
    @Override
    public void incrHotScore(Long skuId, Integer score) {
        //1.先保存数据到缓存中 积累到一定程度 统一更新ES Redis是单线程  原子性操作 使用zset
        /*Double hotScore1 = redisTemplate.opsForZSet().score("hotScore", skuId); //查询score
        int i = hotScore1 + score;
        redisTemplate.opsForZSet().add("hotScore",skuId,i);*/
        Double totalScore = redisTemplate.opsForZSet().incrementScore("hotScore", skuId, score);
        if (totalScore % 10 == 0){
            //1.查询es
            Optional<Goods> byId = goodsDao.findById(skuId);
            Goods goods = byId.get();
            goods.setHotScore(Math.round(totalScore));
            //3:更新ES  底层：先删除 再添加
            goodsDao.save(goods);
        }
        System.out.println("当前分数：" + totalScore);
    }

    //开始搜索
    @Override
    public SearchResponseVo search(SearchParam searchParam) {
        //1.创建索引请求对象 searchRequest 100行
        SearchRequest searchRequest = buildSearchRequest(searchParam);
        //2.执行搜索
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //3.对返回值searchResponse进行结果解析
        SearchResponseVo vo = parseSearchResponse(searchResponse,searchParam);
        return vo;
    }

    //3.对返回值searchResponse进行结果解析
    private SearchResponseVo parseSearchResponse(SearchResponse searchResponse,SearchParam searchParam) {
        SearchResponseVo vo = new SearchResponseVo();
        //1.设置页码数据
        SearchHits hits = searchResponse.getHits();
        long totalHits = hits.getTotalHits();
        System.out.println("总条数:"+totalHits);

        vo.setTotal(totalHits);
        vo.setPageNo(searchParam.getPageNo());
        vo.setPageSize(searchParam.getPageSize());
        //总页数  = （总条数 + 每页数 - 1）/每页数
        long totalPages = (vo.getTotal() + vo.getPageSize() - 1)/vo.getPageSize();
        vo.setTotalPages(totalPages);
        //2.商品集合
        SearchHit[] hits1 = hits.getHits();
        List<Goods> listGoods = Arrays.stream(hits1).map((item) -> {
            //将item转换为字符串
            String itemString = item.getSourceAsString();
            //将字符串转换为goods对象
            Goods goods = JSONObject.parseObject(itemString, Goods.class);
            //高亮显示的解析
            Map<String, HighlightField> highlightFields = item.getHighlightFields();
            if (null != highlightFields && highlightFields.size() > 0){
                HighlightField title = highlightFields.get("title");
                Text[] textArgs = title.getFragments();
                String s = textArgs[0].toString();
                goods.setTitle(s);
            }

            return goods;
        }).collect(Collectors.toList());
        vo.setGoodsList(listGoods);
        //3.品牌集合
        Aggregations aggregations = searchResponse.getAggregations();
        ParsedLongTerms aggTmId = aggregations.get("aggTmId");
        List<SearchResponseTmVo> listTmVo = aggTmId.getBuckets().stream().map((bucket) -> {
            SearchResponseTmVo tmVo = new SearchResponseTmVo();
            //设置id
            long tmId = ((Terms.Bucket) bucket).getKeyAsNumber().longValue();
            tmVo.setTmId(tmId);
            //设置品牌的名称
            ParsedStringTerms aggTmName = ((Terms.Bucket) bucket).getAggregations().get("aggTmName");
            tmVo.setTmName(aggTmName.getBuckets().get(0).getKeyAsString());
            //设置品牌的logo
            ParsedStringTerms aggTmLogoUrl = ((Terms.Bucket) bucket).getAggregations().get("aggTmLogoUrl");
            tmVo.setTmLogoUrl(aggTmLogoUrl.getBuckets().get(0).getKeyAsString());
            return tmVo;
        }).collect(Collectors.toList());
        vo.setTrademarkList(listTmVo);
        //4.平台属性集合
        ParsedNested aggAttrs = aggregations.get("aggAttrs");
        ParsedLongTerms aggAttrId = aggAttrs.getAggregations().get("aggAttrId");
        List<SearchResponseAttrVo> attrVoList = aggAttrId.getBuckets().stream().map((bucket) -> {
            SearchResponseAttrVo attrVo = new SearchResponseAttrVo();
            //attrID
            long attrID = ((Terms.Bucket) bucket).getKeyAsNumber().longValue();
            attrVo.setAttrId(attrID);
            //attrName
            ParsedStringTerms aggAttrName = ((Terms.Bucket) bucket).getAggregations().get("aggAttrName");
            attrVo.setAttrName(aggAttrName.getBuckets().get(0).getKeyAsString());
            //attrValueList
            ParsedStringTerms aggAttrValue = ((Terms.Bucket) bucket).getAggregations().get("aggAttrValue");
            List<? extends Terms.Bucket> buckets = aggAttrValue.getBuckets();
            List<String> attrValueList = buckets.stream().map((item) -> {
                String attrValue = ((Terms.Bucket) item).getKeyAsString();
                return attrValue;
            }).collect(Collectors.toList());
            attrVo.setAttrValueList(attrValueList);
            return attrVo;
        }).collect(Collectors.toList());
        vo.setAttrsList(attrVoList);
        return vo;
    }

    //1.创建索引请求对象 searchRequest 100行
    private SearchRequest buildSearchRequest(SearchParam searchParam) {
        SearchRequest searchRequest = new SearchRequest("goods");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //searchSourceBuilder.query(QueryBuilders.matchAllQuery()); //查询全部

        //构建组合对象
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //1.查询关键词
        String keyword = searchParam.getKeyword();
        if (!StringUtils.isEmpty(keyword)){
            //match: 先分词,再模糊查询 相当于mysql中的like
            boolQueryBuilder.must(
                    QueryBuilders.matchQuery("title",keyword).operator(Operator.AND));
        }else {
            //查询全部
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        }

        //2.查询品牌   入参: 品牌ID:品牌的名称
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)){
            //把入参分开
            String[] split = StringUtils.split(trademark, ":");
            //term 直接查询 相当于mysql中的 =
            boolQueryBuilder.must(QueryBuilders.termQuery("tmId",split[0]));
        }

        //3.一 二 三级分类
        Long category1Id = searchParam.getCategory1Id();
        if (null != category1Id){
            boolQueryBuilder.must(QueryBuilders.termQuery("category1Id",category1Id));
        }
        Long category2Id = searchParam.getCategory2Id();
        if (null != category2Id){
            boolQueryBuilder.must(QueryBuilders.termQuery("category2Id",category2Id));
        }

        Long category3Id = searchParam.getCategory3Id();
        if (null != category3Id){
            boolQueryBuilder.must(QueryBuilders.termQuery("category3Id",category3Id));
        }

        //4.查询平台属性
        //入参: http://url?props=平台ID:平台Name:平台value
        String[] props = searchParam.getProps();
        if (null != props && props.length > 0){
            //儿子的组合对象
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            for (String prop : props) {
                String[] split = prop.split(":");
                if (null != split && split.length == 3){
                    BoolQueryBuilder subQueryBuilder = QueryBuilders.boolQuery();
                    subQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId",split[0]));
                    subQueryBuilder.must(QueryBuilders.termQuery("attrs.attrName",split[2]));
                    subQueryBuilder.must(QueryBuilders.termQuery("attrs.attrValue",split[1]));
                    boolQuery.must(QueryBuilders.nestedQuery("attrs",subQueryBuilder, ScoreMode.None));
                }
            }
            boolQueryBuilder.filter(boolQuery);
        }

        searchSourceBuilder.query(boolQueryBuilder);
        //5.排序  入参: 1:desc/asc    1,2,3 分别为 默认综合 价格 createTime
        String order = searchParam.getOrder();
        String field = "hotScore"; //默认综合排序
        if (!StringUtils.isEmpty(order)){
            String[] split = order.split(":");
            switch(split[0]){
                case "1":
                    break;
                case "2":
                    field = "price";
                    break;
                case "3":
                    field = "createTime";
                    break;
            }
            searchSourceBuilder.sort(field,"asc".equalsIgnoreCase(split[1])? SortOrder.ASC:SortOrder.DESC);
        }else {
            //进行默认排序
            searchSourceBuilder.sort(field,SortOrder.DESC);
        }

        //6.分页   开始行,每页数
        Integer pageNo = searchParam.getPageNo();//当前页数
        Integer pageSize = searchParam.getPageSize();//每页记录数
        searchSourceBuilder.from((pageNo-1)*pageSize);  //开始行
        searchSourceBuilder.size(pageSize);  //每页记录数

        //7.高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title")
                .preTags("<font style='color:red'>")
                .postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);

        //8.聚合
        //8.1: 品牌聚合
        searchSourceBuilder.aggregation(
                AggregationBuilders.terms("aggTmId").field("tmId")
                .subAggregation(AggregationBuilders.terms("aggTmName").field("tmName"))
                .subAggregation(AggregationBuilders.terms("aggTmLogoUrl").field("tmLogoUrl"))
        );

        //8.2: 平台属性聚合
        searchSourceBuilder.aggregation(
                AggregationBuilders.nested("aggAttrs","attrs")
                .subAggregation(AggregationBuilders.terms("aggAttrId").field("attrs.attrId")
                .subAggregation(AggregationBuilders.terms("aggAttrName").field("attrs.attrName"))
                .subAggregation(AggregationBuilders.terms("aggAttrValue").field("attrs.attrValue")))
        );

        //------------------------------------------------------------------------
        System.out.println(searchSourceBuilder);
        searchRequest.source(searchSourceBuilder);
        //设置索引库的名称
        searchRequest.indices("goods");
        //设置 type
        //searchRequest.types("info","hhss");
        return searchRequest;
    }

    @Autowired
    RedisTemplate redisTemplate;
}
